#include "mystrct.h"
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <malloc.h>
#include <string.h>
#include <fcntl.h>

/**
 * @brief 显示表头
 * 
 */
void tableHead()
{
    //输出表头
    fputc('\n', stdout);
    fputc('\n', stdout);
    fputc('\n', stdout);
    fputs("++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n", stdout);
    fputs("+|no           |name         |math         |eng          |total        +\n", stdout);
    fputs("+                                                                      +\n", stdout);
}

/**
 * @brief 过关键字搜索并显示
 * 
 * @param stuList 链表的头指针的指针
 * @param name 关键性-姓名
 */
void search(StuinfoList stuList, char *name)
{
    if ((*stuList) == NULL)
    {
        fputs("null pointer!\n", stdout);
        return;
    }
    //指向头结点
    StuinfoPtr p = (*stuList);
    while (p->next)
    {
        //比较查询
        if (strcmp(p->next->name, name) == 0)
        {
            //输出表头
            tableHead();
            //找到了就显示
            printf("+|%-13d|%-13s|%-13.2f|%-13.2f|%-13.2f+\n", p->next->no, p->next->name, p->next->math, p->next->eng, p->next->total);
            fputs("++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n\n", stdout);
            return; //找到了就可以结束
        }
        p = p->next;
    }
    fputs("\n++++++++++++++++++++      none studentinfo!      +++++++++++++++++++++++\n\n", stdout);
}

/**
 * @brief 删除链表信息并写入文件
 * 
 * @param stuList 链表的头指针的指针
 * @param name 关键性-姓名
 */
void del(StuinfoList stuList, char *name)
{
    if ((*stuList) == NULL)
    {
        fputs("null pointer!\n", stdout);
        return;
    }

    StuinfoPtr p = (*stuList); //p指向头结点
    while (p->next)
    {
        if (strcmp(p->next->name, name) == 0)
        {
            //删除p->next这个数据
            StuinfoPtr q = p->next; //指向被删除节点
            p->next = q->next;      //修改指针删除节点
            free(q);                //释放删除节点的空间
            //修改节点总数
            ((*stuList)->no)--;
            fputs("del success!\n", stdout);
            break; //删除掉就可以结束循环了
        }
        p = p->next;
    }
    fputs("\n++++++++++++++++++++      none studentinfo!      +++++++++++++++++++++++\n\n", stdout);
    //重新写入文件
    writetoFile(stuList);
}

/**
 * @brief 初始化链表函数
 * 
 * @param stuList 链表的头指针的指针
 */
void init(StuinfoList stuList)
{
    if (stuList == NULL)
    {
        fputs("null pointer!\n", stderr);
        return;
    }
    (*stuList) = (StuinfoPtr)malloc(sizeof(Stuinfo));
    if ((*stuList) == NULL)
    {
        fputs("malloc error!\n", stderr);
        return;
    }
    //本节点的no用来记录总的节点数
    (*stuList)->no = 0;
    //头结点的指针域为空
    (*stuList)->next = NULL;
}

/**
 * @brief 对数学成绩和英语成绩求和
 * 
 * @param stuList 链表的头指针的指针
 */
void sum(StuinfoList stuList)
{
    if ((*stuList) == NULL)
    {
        fputs("null pointer!\n", stdout);
        return;
    }
    StuinfoPtr p = (*stuList)->next;
    while (p)
    {
        p->total = p->math + p->eng;
        p = p->next;
    }
}

/**
 * @brief 向链表中插入一个结点
 * 
 * @param stuList 链表的头指针的指针
 * @param stu 插入的学生结点
 */
void insertIntoStuList(StuinfoList stuList, const Stuinfo stu)
{
    if ((*stuList) == NULL)
    {
        fputs("null pointer!\n", stderr);
        return;
    }
    //分配新节点
    StuinfoPtr s = (StuinfoPtr)malloc(sizeof(Stuinfo));
    if (s == NULL)
    {
        fputs("malloc error!\n", stderr);
        return;
    }
    //初始化数据域
    s->no = stu.no;
    //给s->name分配空间
    s->name = (char *)malloc(sizeof(char) * 30);
    strcpy(s->name, stu.name); //字符串的拷贝使用strcpy
    s->math = stu.math;
    s->eng = stu.eng;
    //插入到链表头部
    s->next = (*stuList)->next; //指向第一个实体节点
    (*stuList)->next = s;       //改变头指针
    //节点数+1
    (*stuList)->no++;
}

/**
 * @brief 从控制台接收信息插入链表并写入文件
 * 
 * @param stuList 链表的头指针的指针
 */
void add(StuinfoList stuList)
{
    Stuinfo p;
    //缓冲区
    char buffer[128];
    fputs("Please input test file number:\t", stdout);
    int n;
    scanf("%d", &n);
    //插入3条数据
    for (int i = 0; i < n; i++)
    {
        fprintf(stdout, "Please input data %d:\n", i + 1);
        //接受学号
        fputs("input no:\t", stdout);
        scanf("%d", &(p.no));

        //接受姓名
        //为姓名分配空间
        p.name = (char *)malloc(sizeof(char) * 30); //char[30]的空间
        fputs("input name:\t", stdout);
        //吃掉回车符
        fgetc(stdin);
        fgets(p.name, 30, stdin);
        //处理最后一个字符\n --> \0
        p.name[strlen(p.name) - 1] = '\0';

        //接受数学成绩
        fputs("input math:\t", stdout);
        scanf("%f", &(p.math));

        //接受英语成绩
        fputs("input eng:\t", stdout);
        scanf("%f", &(p.eng));

        //插入链表头部
        insertIntoStuList(stuList, p);
    }
    //求总成绩
    sum(stuList);
    //写入文件
    writetoFile(stuList);
}

/**
 * @brief 将链表信息写入文件
 * 
 * @param stuList 链表的头指针的指针
 */
void writetoFile(StuinfoList stuList)
{
    int f = open("u8002119319.dat", O_CREAT | O_WRONLY, 0664);
    if (f == -1)
    {
        fputs("open file error!\n", stderr);
        return;
    }
    //写入文件
    StuinfoPtr p = (*stuList)->next;
    char *buffer; //缓冲区
    while (p)
    {
        //一条一条写入文件
        buffer = (char *)p;                //将结构体指针转为char* ，即char*指向结构体
        write(f, buffer, sizeof(Stuinfo)); //写入文件
        p = p->next;                       //指向下一个结构体
    }
    //关闭文件
    close(f);
}

/**
 * @brief 读出文件信息显示
 * 
 * @param size 文件中的记录数目
 */
void show(size_t size)
{

    int f = open("u8002119319.dat", O_RDONLY);
    if (f == -1)
    {
        fputs("open file error!\n", stdout);
        return;
    }
    //缓冲区
    StuinfoPtr buffer = (StuinfoPtr)malloc(sizeof(Stuinfo));
    //输出表头
    tableHead();
    for (size_t i = 0; i < size; i++)
    {
        read(f, (char *)buffer, sizeof(Stuinfo));
        printf("+|%-13d|%-13s|%-13.2f|%-13.2f|%-13.2f+\n", buffer->no, buffer->name, buffer->math, buffer->eng, buffer->total);
    }
    fputs("++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n\n", stdout);
    //释放缓冲区
    free(buffer);
    //关闭文件
    close(f);
}
